1,创建线程有且仅有两种方式(Thread类注释有说明):继承Thread类和实现Runnable接口,实现父类(父接口)中的run方法即可
Runnable是任务(业务逻辑)的抽象,Thread是线程唯一的抽象
public class NewThread {
private static class NewThread1 extends Thread{
@Override
public void run() {
super.run();
System.out.println("创建线程之继承Thread");
}
}
private static class NewThread2 implements Runnable{
@Override
public void run() {
System.out.println("创建线程之实现Runnable");
}
}
public static void main(String[] a){
NewThread1 thread1 = new NewThread1();
thread1.start();
NewThread2 thread2 = new NewThread2();
new Thread(thread2).start();
}
}
2,当new出一个线程实例,多次调用start方法时,只有第一次会调用成功,如:
public static void main(String[] a){
NewThread1 thread1 = new NewThread1();
thread1.start();
thread1.start();//多次调用
NewThread2 thread2 = new NewThread2();
new Thread(thread2).start();
}
多次调用会抛出异常
原因:Thread类的start方法,当不是第一次调用时,抛出异常,否则执行native的start0方法
附 Thread类的start方法
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
4,关闭线程
不提倡使用suspend,resume和stop方法
suspend (过时):挂起线程,不占用cpu,但是会占用资源,一直保持对锁的占有,易触发死锁
resume:resume()会将suspend挂起的线程继续执行,当resume先于suspend执行时,suspend的线程将一直挂起,引起死锁
stop(过时):停止线程,带有强制性,不能保证线程占用的资源会正常释放,易产生数据错误
建议用interrupt方法中断线程,本质是通知线程进行中断,设置线程的中断标志位为true,线程中检查中断标志位
isInterrupted() 检查中断标志位,判断是不是有中断
Thread.interrupted() (静态方法)检查中断标志位,判断是不是有中断,并把中断标志位由true改为false(复位)
private static class EndThread extends Thread{
@Override
public void run() {
//需要调用Thread.currentThread()方法来获取系统当前正在执行的一条线程,然后才可以对这个线程进行其他操作
String name = Thread.currentThread().getName();
while (!isInterrupted()){
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
System.out.println("running:"+name+"---interrupt is "+isInterrupted());
//捕获中断异常并抛出时,虽然调用了中断,但是此处会把中断标志位改为false,会导致程序一直运行,需要手动再中断一次
//(猜测)防止有资源没有释放之类的操作,所以手动操作之后进行中断
interrupt();
}
System.out.println("running:"+name);
System.out.println("running:"+name+"---interrupt is "+isInterrupted());
}
System.out.println("running:"+name+"---interrupt is "+interrupted());
}
}
public static void main(String[] a) throws Exception{
EndThread endThread = new EndThread();
endThread.start();
Thread.sleep(400);
endThread.interrupt();
}
不建议使用自建参数进行判定,在方法被阻塞时会不起作用,如:
建议使用方法:
5,线程共享
。。。
6,线程等待与唤醒
wait(long timeout):当前线程进入等待状态,并释放当前线程所持有的锁,timeout为毫秒,当被唤醒之后,才会执行wait后面的代码
notify():唤醒当前对象上的单个线程
notifyAll():唤醒当前对象上是所有线程,建议用notifyAll,因为notify可能发生要通知的线程没通知到 ,一般放在同步代码块的最后执行
sleep(long timeout):使当前线程从运行状态进入到阻塞状态,并不会释放持有的锁,让线程休眠>=timeout的时间,休眠结束后,线程会从阻塞状态变为就绪状态
这几个方法都在Object类中而不在Thread类中
JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了(由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。)
7,ThreadLocal
提供一个变量的副本,线程的隔离。
spring在事务的部分用到了ThreadLocal,让每个线程保存自己的连接,不在同一连接的话不会形成事务
8,注意
8.1 synchronized方法要锁同一个对象
int i = 0;
@Override
public void run() {
synchronized(i){
i++;//此处锁不起作用,因为i++时i已经被重新new了一个对象
//如果要锁的话,可以在外面new一个object或者其他类型的对象放到锁里
System.out.println("创建线程之实现Runnable");
}
}
volatile jdk最轻量的同步机制,只保证可见性(修改被线程发现),不保证原子性(线程安全),适用于一写多读
yield()方法:暂停当前正在执行的线程对象,让出CPU的执行权,不会释放锁,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
jion()方法:线程实例的join()方法可以使得一个线程在另一个线程结束后再执行,即也就是说使得当前线程可以阻塞其他线程执行;
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
回收资源的内容写到finalize方法,但是并不靠谱,他只是一个守护线程,有可能还没调用主线程就停了
并行:可以同时运行的任务数 ,假如电脑有8个逻辑处理器,那就可以同时运行8个线程
并发:交替执行,时间片轮转就是一种并发机制