新建线程的两种方法
继承Thread类并重写run()方法(一般采用匿名内部类)
class ThreadTest extends Thread{
//若用run方法而非start方法开启新线程,他只会在当前线程调用run方法,而不会开启新//线程。
@Override
public void run() {
for (int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+" am running");
}
}
public static void main(String[] args) {
ThreadTest threadTest1=new ThreadTest();
ThreadTest threadTest2=new ThreadTest();
threadTest2.start();
threadTest1.start();
}
}
匿名内部类
使用匿名内部类得到的实例对象,是该类继承某一个类后得到的新的类的实例对象或者派生一个接口所得的新的类的实例对象。匿名内部类中的方法重写了该接口或者父类(即被继承的那个类)中的方法。
class ThreadTest{
public static void main(String[] args) {
Thread threadTest1=new Thread(){
public void run() {
for (int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+"thread1 am running");
}
}
};
Thread threadTest2=new Thread(){
public void run() {
for (int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+" thread2 am running");
}
}
};
threadTest2.start();
threadTest1.start();
}
}
Implements Runnable接口
class ThreadTest implements Runnable{
@Override
public void run() {
for (int i=1;i<10;i++){
System.out.println("go go go");
}
}
public static void main(String[] args) {
Thread t1=new Thread(new ThreadTest());
t1.start();
// Thread t2=new Thread();
// t1.run();
// t2.run();
}
}
注释掉的run()方法,利用idea Ctrl查看源码可知
public void run() {
if (target != null) {
target.run();//这个run()就是Runnable中的run()。
}
}
终止线程
终止线程已不用stop()方法,因为该方法强行将线程停止,会导致数据不一致。
一般通过在线程中自定义一个stopMe()方法,在此方法中将某个类变量改为false,在run方法中增加if判断,若那个类变量为false,则break;。
代码可参考书中P38。
线程中断
class ThreadTest {
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
}
}
};
t1.start();
t1.interrupt();//中断线程,run方法中加入判断语句,
// Thread.currentThread().isInterrupted()返回true时,
//break;退出循环体结束线程。
}
}
注:1.Thread.currentThread表示当前代码段正在被哪个线程调用的相关信息。
2.this表示的是当前对象,与Thread.currentThread有很大的区别
等待和通知
线程A中,调用了obj.wait(方法,那么线程A就会停止继续执行,而转为等待状态。等待到何时结束呢?线程A会一直等到其他线程调用了obj.notify()方法为止。这时,obj对象就俨然成为多个线程之间的有效通信手段。
如果一个线程调用了object.wait(),那么它就会进入object对象的等待队列。这个等待队列中,可能会有多个线程,因为系统运行多个线程同时等待某一个对象。 当object.notify()被调用时,它就会从这个等待队列中,随机选择一个线程,并将其唤醒。这里希望大家注意的是,这个选择是不公平的,并不是先等待的线程会优先被选择,这个选择完全是随机的。
除了notify()方法外,Object 对象还有一个类似的notifyAll()方法,它和notify()的功能基本一致,但不同的是,它会唤醒在这个等待队列中所有等待的线程,而不是随机选择一个。
Object.wait()方法 并不是可以随便调用的。它必须包含在对应的synchronzied语句中,无论是wait()或者notify()都需要首先获得目标对象的一个监视器。T1在正确执行wait()方法前,首先必须获得object 对象的监视器。而wait()方法在执行后,会释放这个监视器。这样做的目的是使得其他等待在object对象上的线程不至于因为T1的休眠而全部无法正常执行。
Wait()和notify()存在于object中,故可直接object.wait();
可参考P43代码如下:
上述代码中,t2 notify t1之后,由于t2持有object监视器,所以t1会等待t2执行wait方法结束之后,t1才能拿到t2释放的监视器并继续执行。
Wait方法执行时,当前线程会等待并释放锁资源,notify方法调用时则会唤醒等待的线程,如果调用notify方法时,等待的线程有好几个,则随机从中唤醒一个线程。Notify方法和wait方法必须在synchronized中使用。
挂起和继续执行
t1.resume(); t1.suspend();
Suspend()会导致线程暂停的同时不会释放任何锁资源。
P46页的代码,resume在suspend之前执行了,所以jstack显示该线程runnable,就是因为resume在suspend之前执行了,所以suspend之后就再也没有resume,所以他是挂起的,并永远挂起。
为什么resume会在suspend之前执行,因为main线程执行到t2.resume的时候,t2.start还没有执行到suspend。
Join和yield
Public static void main{
AddThread at=new AddThread()
At.start()
At.join()//此时main方法会等待AddThread执行完。
}
Start方法开始执行run方法,此时主线程往下执行时碰到join方法,主线程处于wait状态,当start方法执行完毕时,被等待线程(即at.start)将调用notifyall激活等待线程(即主线程),然后主线程继续执行。
Thread.yield()
该静态方法表示当前线程让出CPU。
补:JMM:java memory model
模型的三个特征:
原子性
读取数据可能就出现读串行的情况。
可见性
某个线程修改共享变量的值,另一个应立即知晓。
有序性
避免指令重排的情况出现。
Volatile和JMM
使用volatile声明某个变量,JVM会采取特殊手段,保证这个变量的可见性等特点。
他对保证操作的原子性是有很大帮助的,但是他不能替代锁。
他可以保证可见性和有序性。
线程组
代码
public class Test01 implements Runnable {
public static void main(String[] args) {
ThreadGroup threadGroup=new ThreadGroup("的的");//一个叫的的 的线程组
Thread t1=new Thread(threadGroup,new Test01(),"T1");//将t1存入线程组。
Thread t2=new Thread(threadGroup,new Test01(),"T2");//将t2存入线程组。
t1.start();
t2.start();
System.out.println(threadGroup.activeCount());
threadGroup.list();
}
@Override
public void run() {
String a=Thread.currentThread().getThreadGroup().getName()+"--"+Thread.currentThread().getName();//获取线程组名称和线程名。
while (true){
System.out.println(a);
try {
Thread.sleep(3000);
}
catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
输出
"C:\Program Files\Java\jdk1.8.0_171\bin\java.exe" -javaagent:D:\ideau\ideaIU-2018.1.4\lib\idea_rt.jar=64583:D:\ideau\ideaIU-2018.1.4\bin -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_171\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\rt.jar;D:\ideaProject\xiancheng\out\production\xiancheng" Test01
2
的的--T2
的的--T1
java.lang.ThreadGroup[name=的的,maxpri=10]
Thread[T1,5,的的]
Thread[T2,5,的的] 注:这是list()输出的内容。
的的--T2
的的--T1
守护线程
t.setDaemon(true);
t.start();
t.setDaemon(true);必须在start之前设置,不然守护线程设置会失败。
上述代码t被设置成守护线程,main为用户线程,
用户线程结束后,守护线程要守护的对象也不存在了,故整个应用程序就该结束了。
线程优先级
Thread high=new HighPriority();//HighPriority继承了Thread。
High.setPriority(Thread.MAX_PRIORITY);//也可以传入数字,数字大于10或小于0则报错
High.start();
MIN_PRIORITY NORM_PRIORITY MAX_PRIORITY分别对应1,5,10。
Synchronized关键字
P60的错误范例可看看
常见错误
Int会溢出
Arraylist和hashmap是线程不安全的
integer是不变对象(详情请看P69)