1.线程基础
1)用户线程和主线程main线程的区别:
- 主线程停掉之后,用户线程不会跟着停掉。
- 守护线程会随着主线程停掉之后 停掉。
2)不需要结果返回就叫异步执行,需要结果的返回叫同步(需要步调一致)。
3)
- 多核cpu可以并行执行多个线程,提升效率。但线程太多,会影响cpu资源调度,会影响效率。
- 单核cpu是串行执行,会比较浪费时间。单核cpu上运行多个程序,实际上是并不是并行执行,而是cpu进行快速的切换,让我们觉得是并行运行的。
2.线程创建方式
1.继承Thread方式(不推荐)
2.匿名内部类方式
3.实现Runable接口方式
4.FutureTask方式(可以获得线程返回结果)
1.实现接口方式:
public class RunableClass implements Runnable {
@Override
public void run() {
System.out.println (Thread.currentThread ().getName ()+"正在运行");
}
}
2.FutureTask方式,实现Callable接口:
class CallableTest implements Callable {
@Override
public String call() throws Exception {
String s="sssss";
return s;
}
}
测试类:
public class ThreadTest {
public static void main(String[] args) throws Exception {
//1.匿名内部类方式
Thread t1=new Thread (){
@Override
public void run() {
System.out.println ("t2开始运行");
}
};
t1.start();
//2.实现接口方式
RunableClass r=new RunableClass();
Thread t2=new Thread(r);
t2.start();
}
//3. FutureTask方式
CallableTest callable= new CallableTest ();
FutureTask<String> task = new FutureTask<String> (callable);
Thread t3 = new Thread (task);//task对象传参给Thread构造线程
t3.start ();//开始执行
System.out.println (task.get ());//获取返回的结果为 sssss
}
3.join线程同步
使用join方法,保持线程间的同步。
即一个线程代码运行完之后,才运行下一个线程。
Thread t1=new Thread(RunableClass);
t1.join()//t1线程运行结束后才会运行后面的代码
t1.join(2000);//t1运行2s后,运行后面的代码。若t1 2s内就运行完了 也会继续向后运行 不会等2s
4.interupted
Thread.interrupted () 使打断线程,然后可以根据打断标记做后续处理。与之类似的是 Thread.stop()强制关闭线程(不推荐)
打断sleep,wait,join等状态的线程,会抛出InterruptException异常,打断标记会被清除,isInterrupted判断是不是被打断,可根据这个控制什么时候退出线程。
举例:
设计模式之两阶段终止模式:如果被打断之后,后续处理;没被打断,判断是否异常,异常就设置打断标记,无异常就执行记录。
实现Runable接口:
class InterRupted implements Runnable{
@Override
public void run() {
while (true){
Thread thread = Thread.currentThread ();
//如果被打断 就处理后续一些事 并退出线程
if(thread.isInterrupted ()){
System.out.println ("记录日志,退出线程");
break;
}
//没被打断的话
try {
Thread.sleep (2000);//睡眠中被打断会被捕获异常 并清除打断标记
System.out.println ("继续监视,执行监控记录");
} catch (InterruptedException e) {
e.printStackTrace ();
System.out.println ("打断标记:"+thread.isInterrupted ());
thread.interrupt ();//再次 打断 设置打断标记为true
}
}
}
}
测试类:
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread (new InterRupted (),"t1");
thread.start ();
thread.sleep(10000); //10秒后打断
thread.interrupt ();//打断
}
}
结果分析:
我们看结果,抛出了打断异常,此时打印了 打断标记为false。意思是打断sleep,wait等状态的线程,会抛出打断异常,而且打断标记会被清除。
4.线程安全
- 多线程对共享资源操作,线程出现交错情况下,就会出现问题。
- 一块代码存在对共享资源的多线程读写操作,就是临界区。
(共享资源 在几个线程都操作)
解决办法:
- 阻塞时方式:synchronzied 加 锁
- 非阻塞时方式:原子操作
对临界区的共享资源synchronzied枷锁,使之互斥,可避免线程安全问题。
1.synchronzied加锁实质是保护了synchronzied内的代码块的原子性,这些操作不会被多个线程分拆。
2.synchronzied加在方法上,等同于所在当前对象this枷锁。锁只能锁对象。
public void sell(int count){
synchronzied(this){
this.amount-=count;
}
}
3.如果组合出现两个不同的临界区,需要给两个分别加上锁,因为锁只能锁住相应的对象。
public void sell(int count,Student s){
synchronzied(obj1){
this.amount-=count;
}
synchronzied(obj2)
{
s.count++;
}
}
也可找出两个临界区都共享的对象,进行枷锁。
public void sell(int count,Student s){
synchronzied(公共对象){
this.amount-=count;
s.count++;
}
}