一.线程得创建方式
1.继承Thread类 重写run方法,.start()方法运行
public class ThreadTest extends Thread {
2.实现Runnable接口,重写run方法 .start()运行
public class ThreadTest3 implements Runnable {
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();
}
}
二.类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() {
3.join()方法主要作用是同步,它可以使得线程之间的并行执行变为串行执行。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。相当于是一个插队的作用。
package day07;
public class Demo02 implements Runnable{
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(){}代码块,来给方法或者代码块加锁,保证其线程运行的安全性。
-
修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
-
修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
-
修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
-
修饰一个类,其作用的范围是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();
注意事项:
-
在定义接口方法时不能使用synchronized关键字。
-
构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
-
无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
-
每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
-
实现同步是要很大的性能开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
package day09;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThread implements Runnable {
浙公网安备 33010602011771号