一.线程得创建方式

1.继承Thread类 重写run方法,.start()方法运行

public class ThreadTest extends Thread {
   @Override
   public void run() {
       for (int i = 0; i < 20; i++) {
           System.out.println("看代码"+i);
      }
  }

   public static void main(String[] args) {
       ThreadTest threadTest = new ThreadTest();
       threadTest.start();
       for (int i = 0; i < 200; i++) {
           System.out.println("看视频"+i);
      }
  }
}

2.实现Runnable接口,重写run方法 .start()运行

public class ThreadTest3 implements Runnable {
   @Override
   public void run() {
       System.out.println("重写");
  }
    //创建线程的第二种方法
   public static void main(String[] args) {
       ThreadTest3 test3 = new ThreadTest3();
       Thread thread = new Thread(test3);
       thread.start();
  }
}

3.实现Callable接口 implements Callable<Boolean>,重写call()方法 ,call方法需要有一个泛型返回值

package day06;

import java.util.concurrent.*;

public class caTest implements Callable<String> {
   public static void main(String[] args) {
       caTest caTest1 = new caTest();
       caTest caTest2 = new caTest();
       caTest caTest3 = new caTest();
       //创建线程服务
       ExecutorService ser = Executors.newFixedThreadPool(3);
       //提交执行
       Future<String> submit1 = ser.submit(caTest1);
       Future<String> submit2 = ser.submit(caTest2);
       Future<String> submit3 = ser.submit(caTest3);
       //获取结果
       try {
           String s1 = submit1.get();
           String s2 = submit2.get();
           String s3 = submit3.get();
           System.out.println(s1);
           System.out.println(s2);
           System.out.println(s3);
      } catch (InterruptedException e) {
           e.printStackTrace();
      } catch (ExecutionException e) {
           e.printStackTrace();
      }finally {
           //关闭服务
           ser.shutdown();
      }
  }
   @Override
   public String call() throws Exception {
       return Thread.currentThread().getName();
  }
}

二.类FileUtils 架包名commons-io-2.11.0

package day06;

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

public class FileUtilsTest {
   public static void main(String[] args) {
       // 从网络上下载图片到本地
       try {
           FileUtils.copyURLToFile(new URL("https://upload-images.jianshu.io/upload_images/22797213-5ca7df43c0b1f13f.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/360/h/240"),new File("1.png"));
      } catch (IOException e) {
           e.printStackTrace();
      }
  }
}

三.常用线程方法

Thread.sleep(long):强制线程睡眠一段时间。 thread.start():启动一个线程。 thread.join():在当前线程中加入指定线程,使得这个指定线程等待当前线程,并在当前线程结束前结束。 thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。 thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。 object.wai()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。

1.sleep()方法,是不会释放锁的,sleep 模拟网络的延时,可以放大线程问题,

package day07;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo01 {
   public static void main(String[] args) throws InterruptedException {
       // sleep 模拟网络的延时
       //time(20);
       time2();
  }
   static void time(int a){
       if (a<=0){
           throw new RuntimeException("不符合要求");
      }else {
          while (true){
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(a--);
              if (a<0){
                  break;
              }
          }
      }
  }
   static void time2() throws InterruptedException {
       while (true){
           long l = System.currentTimeMillis();
           System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date(l)));
           Thread.sleep(1000);
      }
  }
}

2.获取当前线程名字,设置线程名

//获取当前线程名
Thread.currentThread().getName();
//setName()设置线程名
new Thread(new Runnable() {
           @Override
           public void run() {

          }
      }).setName();

3.join()方法主要作用是同步,它可以使得线程之间的并行执行变为串行执行。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。相当于是一个插队的作用。

package day07;

public class Demo02 implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i < 1000; i++) {
           System.out.println("vip来了"+i);
      }
  }
   public static void main(String[] args) throws InterruptedException {
       Demo02 demo02 = new Demo02();
       Thread thread = new Thread(demo02);
       thread.start();
       for (int i = 1; i <= 500; i++) {
           System.out.println("排队"+i);
           if (i%100==0) {
               thread.join();
          }
      }
  }
}

4.获取线程状态getState();

NEW:一个尚未启动的线程的状态。也称之为初始状态、开始状态。 RUNNABLE:一个可以运行的线程的状态,可以运行是指这个线程已经在JVM中运行了,但是有可能正在等待其他的系统资源。也称之为就绪状态、可运行状态。 BLOCKED:一个线程因为等待监视锁而被阻塞的状态。也称之为阻塞状态。 WAITING:一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有三种,分别是调用Object.wait()、join()以及LockSupport.park()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。 TIMED_WAITING:一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有五种,分别是:Thread.sleep(long)、Object.wait(long)、join(long)、LockSupport.parkNanos(obj,long)和LockSupport.parkUntil(obj,long)。 TERMINATED:一个完全运行完成的线程的状态。也称之为终止状态、结束状态。

package day07;

public class Demo03 {
   public static void main(String[] args) {
       Thread thread = new Thread(()->{
           for (int i = 0; i < 5; i++) {
               try {
                   Thread.sleep(1000);
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
               System.out.println("123456");
          }
      });
       thread.start();
       // 获取线程状态
       Thread.State state = thread.getState();
       System.out.println(state);
       while (state!=Thread.State.TERMINATED){
           try {
               Thread.sleep(100);
          } catch (InterruptedExc eption e) {
               e.printStackTrace();
          }
           System.out.println(state);
           state=thread.getState();
      }
//       线程一旦死亡 就无法再次调用 否则会报 java.lang.IllegalThreadStateException 异常
     // thread.start();
  }
}

5.线程的优先级 -->优先级只能在一定程度上 让线程优先有更多运行时间,并不是一定绝对优先,具体看cpu的调度。

package day07;

/**
* MIN_PRIORITY = 1;
* NORM_PRIORITY = 5;
* MAX_PRIORITY = 10;
*/
public class Demo04 implements Runnable {
public static void main(String[] args) {
// main方法的优先级 默认值5
System.out.println("main-->"+Thread.currentThread().getPriority());
Demo04 demo04 = new Demo04();
Thread t1 = new Thread(demo04);
Thread t2 = new Thread(demo04);
Thread t3 = new Thread(demo04);
Thread t4 = new Thread(demo04);
Thread t5 = new Thread(demo04);
// 线程优先级需要先设定再 start();
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(7);
t4.start();
t5.setPriority(Thread.MAX_PRIORITY);
t5.start();
/**
* 优先级只能在一定程度上 让线程优先进行,并不是一定优先,具体看cpu的调度
* main-->5
* Thread-0-->5
* Thread-2-->4
* Thread-4-->10
* Thread-3-->7
* Thread-1-->1
*/
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}

6.好用的Lambda 语法-->

1.必须在函数式接口中

2.函数式接口--------接口中只有一个抽象方法

3.当形式参数只有一个时,返回语句只有一条时 分别可以省略小括号,大括号

package day07;

public class LambdaTest {
public static void main(String[] args) {
/**
* lambda 语法
* 必须在函数式接口中
* 函数式接口--------接口中只有一个抽象方法
*/
love il =null;
/* il = (a) -> {
System.out.println("我爱你2--->" + a);
};*/
//简化 当形式参数只有一个时,返回语句只有一条时 省略小括号,大括号
il = a -> System.out.println("我爱你2--->" + a);
il.kk(520);
}
static class ILove implements love {
@Override
public void kk(int a) {
}
}
interface love {
void kk(int a);
}
}

7.线程之间通信方法

wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。

notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。

notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。

package day08;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
public static void main(String[] args) {
BuyTickets buyTickets = new BuyTickets();
new Thread(buyTickets).start();
new Thread(buyTickets).start();
new Thread(buyTickets).start();
}
}
class BuyTickets implements Runnable{
int totalTickets=10;
// ReentrantLock 不同于Synchronized 显式得定义范围 通过lock(),unlock()方法
static final ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
lock.lock();
try {
while (true){
if (totalTickets>0){
System.out.println(totalTickets--);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}finally {
lock.unlock();
}

}
}

四.线程的不安全性

1.不安全的ArrayList

public class UnSafeArrayList {
public static void main(String[] args) {
List<String> arr = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
synchronized(arr){
arr.add(Thread.currentThread().getName());
}
}).start();
}
try {
// 稍微等一下,等线程跑完,等一万条线程进入死亡状态
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// ArrayList 不安全,因为线程执行速度快,插入时容易覆盖之前存入得数据。
System.out.println(arr.size()); //9998
}
}
解决办法---->1 使用List安全集合 -->CopyOnWriteArrayList
package day08;

import java.util.concurrent.CopyOnWriteArrayList;

public class TestJUC {
/**
*
* @param args
* @throws InterruptedException
* java.util.concurrent.CopyOnWriteArrayList;
* 是一个安全得List集合和ArrayList不同,但是相对的性能上可能不如ArrayList
* 同样得一万条线程add得问题他不会产生插入覆盖问题
*/
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> strings = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
strings.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(strings.size());
}
}
解决办法--->2 使用同步锁,使得线程排队插入集合
package day08;

import java.util.ArrayList;
import java.util.List;


public class UnSafeArrayList {
public static void main(String[] args) {
List<String> arr = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
synchronized (arr){
new Thread(() -> {
synchronized(arr){
arr.add(Thread.currentThread().getName());
}
}).start();
}

}
try {
// 稍微等一下,等线程跑完,等一万条线程进入死亡状态
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// ArrayList 不安全,因为线程执行速度快,容易覆盖之前存入得数据。
System.out.println(arr.size()); //9998
}
}

2.不安全的买票方法

package day08;

public class UnSafeBuyTickets {
public static void main(String[] args) {
BuyTickets buyTickets = new BuyTickets();
Thread t1 = new Thread(buyTickets,"wo");
Thread t2 = new Thread(buyTickets,"ta");
Thread t3 = new Thread(buyTickets,"它");
t1.start();
t2.start();
t3.start();
}
static class BuyTickets implements Runnable{
private int totalTickets=10;
private boolean flat=true;
@Override
public void run() {
while (flat){
if (totalTickets<=0){
System.out.println("票已经抢完");
flat=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到票" + totalTickets--);
}
}
}
}
同样的解决方法也可以用加锁的方式解决,比如:
public synchronized void run() {
while (flat){
if (totalTickets<=0){
System.out.println("票已经抢完");
flat=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到票" + totalTickets--);
}
}

同步锁synchronized 的应用

使用synchronized 方法或者synchronized(){}代码块,来给方法或者代码块加锁,保证其线程运行的安全性。
  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

  3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

  4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

1.当有一个明确的对象作为锁时

我们用synchronized 给obj对象加了锁。这时,当一个线程访问obj对象时,其他试图访问obj对象的线程将会阻塞,直到该线程访问obj对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。

 //obj 锁定的对象
synchronized(obj)
{

}

2.没有明确的对象加锁时,只想对代码块加锁时

说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

class Test implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
public void method()
{
synchronized(lock) {
// todo 同步代码块
}
}

public void run() {

}
}

3.synchronized修饰一个方法

@Override
public synchronized void run() {
while (flat){
if (totalTickets<=0){
System.out.println("票已经抢完");
flat=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到票" + totalTickets--);
}

或者 两者等价

@Override
public void run() {
synchronized{

}
}

4.synchronized 是不可以继承的

父类中的synchronized方法,子类是无法继承的,重写也是默认不同步的,需要显式的添加synchronized关键字。

class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public synchronized void method() { }
}

还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。

class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public void method() { super.method(); }
}

5.显式的定义锁Lock 这里使用Lock的实现类ReentraLock

本质上和synchronized 相同并没有大区别

package day08;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
   public static void main(String[] args) {
       BuyTickets buyTickets = new BuyTickets();
       new Thread(buyTickets).start();
       new Thread(buyTickets).start();
       new Thread(buyTickets).start();
  }
}
class BuyTickets implements Runnable{
    int totalTickets=10;
//     ReentrantLock 不同于Synchronized 显式得定义范围 通过lock(),unlock()方法
    static final ReentrantLock lock=new ReentrantLock();
   @Override
   public void run() {
       lock.lock();
       try {
           while (true){
               if (totalTickets>0){
                   System.out.println(totalTickets--);
                   try {
                       Thread.sleep(10);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
              }else {
                   break;
              }
          }
      }finally {
           lock.unlock();
      }

  }
}

注意事项:

  1. 在定义接口方法时不能使用synchronized关键字。

  2. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

  3. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

  4. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

  5. 实现同步是要很大的性能开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

     

五.线程池

package day09;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThread implements Runnable {
   @Override
   public void run() {
       System.out.println("100");
  }

   public static void main(String[] args) {
       // MyThread myThread = new MyThread();
//       创建线程池 ()池子大小
       ExecutorService executorService = Executors.newFixedThreadPool(5);
       //执行
       executorService.execute(new MyThread());
       executorService.execute(new MyThread());
       executorService.execute(new MyThread());
       executorService.execute(new MyThread());
       //关闭服务
       executorService.shutdown();
  }
}
posted on 2021-12-05 17:46  Forever、H  阅读(209)  评论(0)    收藏  举报