java之线程(线程的创建方式、java中的Thread类、线程的同步、线
CPU:10核 主频100MHz ? ? ? ? ? ? 1核 ?主频 ? ?3GHz 那么哪一个CPU比较好呢? CPU核不是越多越好吗?并不一定。主频用于衡量GPU处理速度的快慢,举个例子10头牛运送货物快还是1架飞机运算货物快?显然是1架飞机,因此1核3GHz的CPU较好,当然,在相同主频的情况下,CPU当然是越多越好。 在Java中,JVM虚拟机允许运行多个线程,他通过java.lang.Thread类来实现 Thread类特性:
构造方法:
一、创建线程的两种方式:
package testThread; public class TestThread extends Thread{ void run() { System.out.println("多线程运行的代码"); for(int i=0;i<10;i++) { System.out.println("这是多线程的逻辑代码"); } } } class Test { static main(String[] args) { Thread t0 = new TestThread(); //在开启了线程后,线程与main()方法并行运行 t0.start();启动线程 } } 2.实现Runnable接口
class TestRunnable implements Runnable{ @Override run() { TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"开始运行"); } } } new Thread( TestRunnable()); t0.start(); Thread t1 = new TestRunnable(),"线程t1"); t1.start(); } } 对于这种方式创建线程,可以给每个线程进行命名,否则默认为Thread-num。 利用实现方式:避免了单继承的局限性、多个线程可以共享同一个接口实现类对象,非常适合多个相同线程来处理同一份资源。 二、Thread类的相关方法 (1)基础方法 void start():启动线程 run():线程在被调度时执行的操作名称 String getName():返回线程的名称 void setName(String name):设置线程的名称 static currentThread():返回当前线程 (2)优先级方法
getPriority():获得优先级 setPriority(int newPriority):设置优先级 线程创建时继承父线程的优先级 (3)其它方法 static void yield():线程让步
join():当某个程序执行流中调用其它线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完毕为止。
static void sleep(long millis):指定时间,毫秒
stop():强制线程生命周期结束 boolean isAlive():判断线程是否还活着 三、线程的生命周期 JDK中用Thread.State来表示线程的状态,包括: 新建:声明并实例化之后; 就绪:执行start之后; 运行:得到cpu的使用权,run()方法开始运行; 阻塞:run()方法停止执行,处于等待状态; 死亡:线程完成了全部工作或被提前终止; ? 四、线程的同步 问题:假设账户上有4000,现在有两个线程,分别各取2000,由于这两个线程是并行的,因此都可能取成功,此时账户上就是-1000了,这显然是不合法的。因此,要引入线程的同步,所谓同步,并不是指同时运行,而是指协同步伐,也就是线程按先后顺序依次执行,这样当取出2000后,账户剩余1000,再进行取2000就不会成功了。 package testThread; Test2 { main(String[] args) { Account account = Account(); User u_weChat = new User(account,2000); User u_zhifubao = ); Thread wechat = new Thread(u_weChat,1)">"微信"); Thread zhifubao = new Thread(u_zhifubao,1)">支付宝); wechat.start(); zhifubao.start(); } } Account{ int money = 3000; void get(int m) { String thread = Thread.currentThread().getName(); System.out.println(正在运行:"+thread); if(money<m) { System.out.println(thread+操作-账户金额不足:" + money); }else { System.操作-账户原有金额:money); System.操作-取款金额:m); money = money - m; System.操作-取款后的余额:money); } } } User implements Runnable{ Account account; money; public User(Account account, money) { this.account = account; this.money = money; } @Override TODO Auto-generated method stub account.get(); } } 输出: 正在运行:微信 此时,正如以上我们所说的,取款不合法了,那么如何解决呢?可以给方法加上同步锁。但是,需要注意的是: Account(); Account account1 = Account(); 输出: 正在运行:微信 因为此时不同对象获得的是不同的锁,所以这种方式并不行,那么如何进行改动呢?只需要使用同一个对象即可,即: Account account = 2000);
此时输出: 正在运行:微信 同时,还可以进一步简化: money);
}
}
在最后调用时,只需要在run()方法中调用account.get()即可,不用将两个方法分开来写。 获得锁的线程会执行完毕后,才将锁交给下一个线程继续执行。 对于在静态方法上加锁: Account(); 对于这种方式,即使传入的是该类的不同对象,仍然获得的是同一个锁。 正在运行:微信 还有一种方式是利用同步锁修饰代码块: void get2( m) { 输出: 正在运行:支付宝 想要不同的对象有不同的锁: ; void get3( m,Account a) { 表示通过方法的参数传递进来的对象的代码块加了同步锁 不同的对象有不同的同步锁 synchronized (a) { String thread = account.get3(money,account); } } 输出: 正在运行:微信 此时,这种加锁方式就没有效果了,与最开始的那种是一样的。 总而言之,要想使加锁有效果,必须获得的是同一个对象的锁。 五、线程之间的通信 wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问。 notify():唤醒正在排队等待同步资源的线程中优先级较高者结束等待。 notifyAll():唤醒正在排队等待资源的所有线程结束等待。 java.lang.Object提供的三个方法只有在sychronized方法或synchronized代码块中才能使用。 对于第四节提到到最后一种方式,只要将使用同一个Account对象,加锁机制就还是成功。但是我们会发现,之前的结果都是微信在支付宝之前进行操作,假设我们要让支付宝先操作,又应该怎么办呢?这时就需要利用线程之间的通信。 void get4( Thread.currentThread().getName(); 如果是微信操作,则暂时挂起,让支付宝先操作 if (thread.equals("微信")){ try { a.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("正在运行:"+thread); money); } if (thread.equals("支付宝")){ a.notify(); } } } } try { account.get4(money,account); } catch (InterruptedException e) { TODO Auto-generated catch block e.printStackTrace(); } } } 我们先让线程为“微信”的暂时挂起,先执行其它的,也就是支付宝,然后当支付宝执行完毕之后告知微信,这样就可以了。 输出: 支付宝操作-账户原有金额:3000 这时,我们又想到,假设我们现在传入的是不同的Account对象呢? 我们先看下输出结果: 支付宝操作-账户原有金额:3000 这说明了什么?支付宝运行结束后并没有成功告知微信,微信一直处于等待状态,其原因在于它们拥有的是不同对象的锁,因此之间不能通信。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |