线程是cpu的最小执行单位
1.实现线程的方式有两种
I.继承Thread类
public class MyThread extends Thread{
public void run(){
try{
Thread.sleep(500);
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("this is MyThread!");
}
}
II.实现Runnable接口
public class ThreadB implements Runnable{
@Override
public void run() {
try{
Thread.sleep(1000);
}catch(Exception ex){
ex.printStackTrace();
}
System.out.println("This is ThreadB");
}
}
2.线程一些常用方法
start() 启动线程,线程进入就绪状态
interrupt() 中断线程
isAlive() 判断进程是否处于活动状态。一般和interrupt()连用
getName() 得到进程名
sleep() 让进程休眠,但是不会释放锁,和wait()方法的区别之一,sleep()是 Thread 的方法,wait()是Object类的方法
yield() 暂停当前线程,并执行其他线程,如果当前线程的优先等级比其他线程都高,可能还会再次进入此线程
join() 等待当前线程结束
getThreadCroup()得到线程组
interrupt()例子
public class StopThread2 implements Runnable{@Override
public void run() {
boolean flag = true;
while(flag){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
//e.printStackTrace();
break;
}
if(Thread.currentThread().isAlive()){
flag=!flag;
System.out.println("StopThread2 stop");
}
}
}
public static void main(String[] args) {
Thread thread = new Thread(new StopThread2(),"StopThread2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main start");
thread.start();
System.out.println("StopThread2 start");
//当线程调用次方法后,线程将销毁,在run()方法中可以调用 isAlive()方法来判断线程是否销毁
thread.interrupt();
System.out.println(thread.isAlive());
System.out.println(thread.isInterrupted());
}
}
join例子
import java.util.concurrent.TimeUnit;
/**
* join 等待该线程终止
* 当在主线程当中执行到t1.join()方法时,就认为主线程应该把执行权让给t1,直到t1执行结束,主线程才能在执行。
* @author Administrator
*
*/
public class JoinTest implements Runnable{
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("zzz");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JoinTest jt = new JoinTest();
Thread t = new Thread(jt);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("this is main thread");
}
}
3.线程几种状态之间的关系
4.守护线程
守护线程简单理解-->在后台运行的线程 eg:jvm垃圾回收,守护线程会在进程结束时自动结束,不需要interrupt()
守护线程 setDeamon(true) 需要在start()方法之前调用
5.线程组 ThreadGroup
线程组表示一个线程的集合。此外,线程组也可以包含其他线程组。线程组构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组。
允许线程访问有关自己的线程组的信息,但是不允许它访问有关其线程组的父线程组或其他任何线程组的信息。
6.线程异常处理
在线程的run()方法中是不能throw 一个异常的,所以在run()方法中的所有异常都只能try catch处理掉,有一些运行时异常是没办法捕获的,所以需要定义一个处理异常的方法
setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 设置该线程由于未捕获到异常而突然终止时调用的处理程序。
public class MyThread implements Runnable{
@Override
public void run() {
int i = 1/0;
}
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t = new Thread(mt,"MyThread");
t.setUncaughtExceptionHandler(new ThreadExceptionDeal());//设置未定义的异常处理方法
t.start();
}
}
import java.lang.Thread.UncaughtExceptionHandler;
/**
* 未捕获的异常处理
* @author Administrator
*
*/
public class ThreadExceptionDeal implements UncaughtExceptionHandler{
/**
* 发生未捕获的异常时进入的默认方法
*/
@Override
public void uncaughtException(Thread arg0, Throwable arg1) {
System.out.println("Thread name is "+Thread.currentThread().getName());
System.out.println("Thread id is "+Thread.currentThread().getId());
arg1.printStackTrace(); //输出异常信息
}
}
setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh); 设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。在有多个线程的时候,我们可能没有一一指定未捕获异常处理方法,可以用这个方法统一处理。
7.线程安全
I.synchronized
/**
* 懒汉式* @author Administrator
*/
public class MySingleton {
private static MySingleton mySingleton = null;
private MySingleton(){};
public static MySingleton getMySingleton(){
if(mySingleton==null){
return new MySingleton();
}
return null;
}
}
在多线程的环境下运行这个程序会出现两个线程同时进入if判断,然后new了两个对象,所以就不是单列了。所以需要用到关键字
public MySingleton getMySingleton(){
synchronized (this) {
if(mySingleton==null){
return new MySingleton();
}
return null;
}
}
这样的话保证了一次只有一个线程进入到if中所以实现了单列。
synchronized 可以修饰方法或者代码块,上面列子就是修饰代码块。
synchronized 性能最优的写法
private static byte [] b = new byte[1];
private static MySingleton mySingleton = null;
public static MySingleton getMySingleton(){
synchronized (b) {
if(mySingleton==null){
return new MySingleton();
}
return null;
}
}
因为获取锁和释放锁都需要对象资源,所以对象越小性能越优
II.Lock
Lock是一个接口,实现类有ReentrantLock、ReentrantReadWriteLock.WriteLock、ReentrantReadWriteLock.ReadLock
Lock接口的方法有
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock锁
* @author Administrator
*
*/
public class ThreadLock implements Runnable{
private ReentrantLock rl = new ReentrantLock();
@Override
public void run() {
try {
//rl.lock();
System.out.println("Thread name is"+Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("Thread name is"+Thread.currentThread().getName());
} catch (InterruptedException e) {
//e.printStackTrace();
}finally {
//rl.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ThreadLock tl = new ThreadLock();
Thread t = new Thread(tl,"123");
Thread t1 = new Thread(tl,"456");
t.start();
t1.start();
Thread.sleep(10000);
}
}
Lock 实现单列
import java.util.concurrent.locks.ReentrantLock;
/**
* 懒汉式
* @author Administrator
*/
public class MySingleton {
private static MySingleton mySingleton = null;
private static byte[] b = new byte[1];
private MySingleton(){};
static ReentrantLock rl = new ReentrantLock();
public static MySingleton getMySingleton(){
/*synchronized (b) {
if(mySingleton==null){
return new MySingleton();
}
return null;
}*/
if(mySingleton == null){
rl.lock();
if(mySingleton == null){
mySingleton = new MySingleton();
}
rl.unlock(); //使用Lock时,一定要有解锁方法
}
return mySingleton;
}
}
III 原子操作 atomic
8.阻塞队列
队列:用于保存一组数据,存取为先进先出
双端队列:队列两端都可以存取数据,如果关闭队列一端的存取,那么久形成了栈的存取模式,先进后出
阻塞队列:当队列为空时,取数据的时候回等待,当队列满了的时候,存数据会等待队列有空间在存入,常用于生产者消费者模式
I.ArrayBlockingQueue 数组结构组成的有界阻塞队列。
此队列按照先进先出(FIFO)的原则对元素进行排序,但是默认情况下不保证线程公平的访问队列,即如果队列满了,那么被阻塞在外面的线程对队列访问的顺序是不能保证线程公平(即先阻塞,先插入)的
import java.util.concurrent.ArrayBlockingQueue;
/*** 数组类型阻塞队列 一个队列,两个线程,一个线程存,一个线程取
* @author Administrator
*
*/
public class ArrayBlockingQue {
ArrayBlockingQueue<Object> abq = new ArrayBlockingQueue<>(10,true);//容积为10的阻塞队列,FIFO
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQue que = new ArrayBlockingQue();
ArrayBlockingQueThread abqt1 = new ArrayBlockingQueThread(que);
Thread t1 = new Thread(abqt1);
ArrayBlockingQueThreadB abqt2 = new ArrayBlockingQueThreadB(que);
Thread t2 = new Thread(abqt2);
t1.start();
t2.start();
Thread.sleep(10000);
}
public boolean add(Object e){
return abq.add(e);//存入一个数据
}
public Object get(){
Object o = new Object();
try {
o = abq.take();//获取队列头部数据,并移除
} catch (InterruptedException e) {
e.printStackTrace();
}
return o;
}
}
public class ArrayBlockingQueThread implements Runnable{
private ArrayBlockingQue abq;
public ArrayBlockingQueThread(ArrayBlockingQue abq){
this.abq = abq;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 15; i++) {
abq.add(i);
System.out.println("第"+(i+1)+"次存入数据为:"+i);
}
}
}
public class ArrayBlockingQueThreadB implements Runnable{
private ArrayBlockingQue abq;
public ArrayBlockingQueThreadB(ArrayBlockingQue que){
this.abq = que;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 15; i++) {
Object object = abq.get();
System.out.println("获取队列数据"+object);
}
}
}
运行结果
也有可能会出现异常
当线程2没有抢到cpu的时候,线程1一直往队列里面插入,就会抛异常
II.DelayQueue
DelayQueue是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。我们可以将DelayQueue运用在以下应用场景
- 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
- 定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。
import java.util.concurrent.DelayQueue;
/*** 阻塞队列测试
* @author Administrator
*
*/
public class DelayQueueTest {
public static void main(String[] args) {
final DelayQueue<Student> dq = new DelayQueue<>();//存学生的延时阻塞队列
for (int i = 0; i < 5; i++) {
//使用随机数让队列第一个值不一定是学生0
Student s = new Student("学生"+i,Math.round(Math.random()*10+i));
// dq.put()时会调用 Delay.compareTo()来进行排序,把延时即将到期的对象放最前面
dq.put(s);
}
//执行时,每次输出的内容不一样
System.out.println(dq.peek().getName());
List<Student> list = new ArrayList<>();
//把队列中四个元素放到list中
dq.drainTo(list, 4);
for (Student student : list) {
System.out.println(student.getName());
}
}
/**
*
* @author Administrator
*
*/
public class Student implements Delayed{
private String name;
private Long workTime;
private Long submitTime;
public String getName(){
return this.name+"交卷用时:"+workTime;
}
public Student(String name,Long submitTime){
this.name = name;
this.workTime = submitTime;
//把6按照分钟转换成秒,也就是说,6分钟转成秒返回值为360
//long convert = TimeUnit.SECONDS.convert(6, TimeUnit.MINUTES);
//System.out.println(convert);
//NANOSECONDS 纳秒 MILLISECONDS 毫秒
this.submitTime=TimeUnit.NANOSECONDS.convert(submitTime,TimeUnit.MILLISECONDS)
+System.nanoTime();
}// 比较此对象与指定对象的顺序。
@Override
public int compareTo(Delayed d) {
Student s = (Student)d;
return submitTime>s.submitTime?1:(submitTime<s.submitTime?-1:0);
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(submitTime-System.nanoTime(), unit.NANOSECONDS);
}
}
III.CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 计数器测试
* @author Administrator
*
*/
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(3);
Worker w1 = new Worker(cdl);
Thread t1 = new Thread(w1);
Worker w2 = new Worker(cdl);
Thread t2 = new Thread(w2);
Worker w3 = new Worker(cdl);
Thread t3 = new Thread(w3);
t1.start();
t2.start();
t3.start();
try {
cdl.await();//等待三个线程结束才执行后面的
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完成");
}
}
class Worker implements Runnable{
private CountDownLatch cdl;
public Worker(CountDownLatch cdl){
this.cdl = cdl;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始工作");
try {
TimeUnit.SECONDS.sleep(1);//睡眠一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束工作");
cdl.countDown();//计数器减1
//System.out.println("计数器剩余值"+cdl.getCount());
}
}
IV. Semaphore 计数信号量
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 计数信号量
* @author Administrator
*
*/
public class SemaphoreTest implements Runnable{
final Semaphore s = new Semaphore(2,true);//一个FIFO的只能最多二个线程进行访问的信号量
public static void main(String[] args) {
SemaphoreTest st = new SemaphoreTest();
for (int i = 0; i < 5; i++) {
Thread t = new Thread(st);
t.start();
}
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("线程名称"+Thread.currentThread().getName());
s.acquire();//获取执行许可
System.out.println("线程名称"+Thread.currentThread().getName()+"开始使用资源");
TimeUnit.SECONDS.sleep(2);
System.out.println("线程名称"+Thread.currentThread().getName()+"执行完成");
s.release();//释放许可
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}