JAVAday23 多线程
一 多线程基本概念
1.1进程的概念
在了解线程之前先要了解进程,进程就是正在运行的程序,是系统进行资源分配和调度的独立单元。每个进程都有自己的内存空间和系统资源,电脑上可以有多个进程,单核cpu在一个时间点上只有一个进程在运行。但是cpu可以在多个进程间高速切换,感觉像是在同时运行。
1.2线程的概念
线程依赖于进程,进程开启后会有多个任务,每个任务就是线程。线程有随机性,回去抢占cpu的执行权。
并行:逻辑上的同时发生,指应用能够同时执行不同的任务。
并发:物理上的同时发生,指应用能够交替执行不同的任务,在一个时间点上运行多个任务。
JVM是多线程,一个是主线程,另一个是垃圾回收线程。
二 如何开启一个多线程
1.1可以使用JAVA提供的Thread这个类来创建线程。
(1)首先定义一个类,继承thread这个类
(2)重写thread的run方法
(3)创建类的对象
(4)开启这个线程
public class MyThread extends Thread{
@override
public void run(){
//run方法就是需要线程来执行的代码,一般耗时的操作,我们就会写在run方法里面,让线程去执行
for(int i= ,i<100,i++){
System.out.println(i);
}
}
}
public class MyText{
public static void main (String [] args){
MyThread th = new MyThread();
//开启线程不是调用run方法,调用run方法就是你是用一个对象,调用一个方法,让这个方法执行,线程并没有开启。
//正确开启线程的方式是调用start(),由线程去调用run方法,执行run方法的代码,同一个线程不要多次开启
th.start();
}
}
1.2设置线程名称和优先级
public class MyThread extends Thread{
@override
public void run(){
for(int i= ,i<100,i++){
//获取线程的名字
String name = new this.name
System.out.println(i);
}
}
}
public class MyText{
public static void main (String [] args){
Thread thread = Thread.currentThread();//获取当前线程对象
String name = thread.getName();
MyThread th = new MyThread();
MyThread th1 = new MyThread();
//设置线程名称
th.setName("线程一");
th1.setName("线程二");
th.start();
th1.start();
}
}
优先级
public class MyText{
public static void main (String [] args){
//java使用线程的调度模型,是抢占式调度
//如果多个线程的优先级一样,线程的执行就是随机抢占
MyThread th = new MyThread();
MyThread th1 = new MyThread();
//设置线程的优先级
th.setPriority(Thread.MAX_PRIORITY);
th1.setPriority(Thread.MIN_PRIORITY);
//获取线程的优先级
int priority = th.getPriority();
int priority1 = th1.getPriority();
System.out.println(priority);
System.out.println(priority1);
//默认优先级都是5
th.start();
th1.start();
th.start();
}
}
1.3线程休眠和线程加入
public class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
String name = Thread.currentThread().getName();
//让线程休眠 就是让线程处于阻塞状态
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 1000; i++) {
System.out.println(name);
}
}
}
public class MyText {
public static void main(String[] args) throws InterruptedException {
//通过构造给线程设置名字
MyThread th = new MyThread("线程1");
th.start();
Thread.sleep(3000);//让当前线程休眠
}
}
public class MyText {
public static void main(String[] args) throws InterruptedException {
MyThread th = new MyThread("线程1");
MyThread th1 = new MyThread("线程2");
MyThread th2 = new MyThread("线程3");
//串行 就是按照顺序去执行
th.start();
th.join();
//join方法在线程开启之后去调用
th1.start();
th1.join();
th2.start();
th2.join();
}
}
public class MyText {
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().setName("主线程");
MyThread th = new MyThread("线程1");
MyThread th1 = new MyThread("线程2");
MyThread th2 = new MyThread("线程3");
//守护线程
th.setDaemon(true);
th1.setDaemon(true);
th2.setDaemon(true);
System.out.println("主线程挂了");
}
}
1.4使用线程复制文件
public class CopyThread extends Thread{
@Override
public void run() {
//我复制第一个文件
try {
Files.copy(Paths.get("D:\\HBuilderX.1.3.2.20181214.full.zip"), Paths.get("E:\\HBuilderX.1.3.2.20181214.full.zip"), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class CopyThread2 extends Thread{
@Override
public void run() {
//我复制第二文件
try {
Files.copy(Paths.get("MyTest.java"), Paths.get("E:\\hehe.txt"), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class MyTest {
public static void main(String[] args) throws IOException {
//一个线程复制一个 文件
CopyThread th1 = new CopyThread();
th1.start();
//一个线程复制一个 文件
new CopyThread2().start();
}
1.5 创建线程的其他两种方式
创建线程的第二种方式
(1)创建一个类,实现Runable接口,重写接口中的run方法
(2)创建Thread类,对象,将Runable接口的子类对象传递进来
(3)调用start()方法开启线程
实现接口的同时可以继承其他类,避免了由于java单继承带来的局限性
public class MyRunable implements Runnable{
@Override
public void run() {
//获取当前线程的名字
String name = Thread.currentThread().getName();
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
public class MyText {
public static void main(String[] args) {
MyRunnable runable = new MyRunnable();
Thread th = new Thread(runable);
th.setName("线程名");
th.start();
}
创建线程的第三种方式
public class MyCallable1 implements Callable {
@Override
public Object call() throws Exception {
System.out.println("线程执行了");
return null;
}
}
public class MyText {
public static void main(String[] args) {
MyCallable1 callable1 = new MyCallable1();
FutureTask<Integer> task = new FutureTask<Integer>(callable1);
Thread thread = new Thread(task);
thread.start();
}
}
三 买票实例以及同步代码块解决线程安全问题
3.1Thread类实现
public class CellThread extends Thread{
static int piao = 100;//设置为静态变量就可以共享这100张票,否则每个成员都可以获得100张
@Override
public void run() {
while (true){
if(piao>1){
String name = this.getName();
System.out.println(name+"正在出售"+piao--);
}
}
}
}
public class MyText {
public static void main(String[] args) {
CellThread th1 = new CellThread();
CellThread th2 = new CellThread();
CellThread th3 = new CellThread();
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
th1.start();
th2.start();
th3.start();
}
}
3.2用runable接口实现
public class CellRunable implements Runnable {
int piao = 100;
@Override
public void run() {
while (true) {
if (piao > 1) {
System.out.println("正在出售" + piao--);
}
}
}
public class MyText {
public static void main(String[] args) {
CellRunable runable1 = new CellRunable();
CellRunable runable2 = new CellRunable();
CellRunable runable3 = new CellRunable();
Thread th1 = new Thread(runable1);
Thread th2 = new Thread(runable1);
Thread th3 = new Thread(runable1);
//三个线程同时执行一个任务,所以不用加静态
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
th1.start();
th2.start();
th3.start();
}
}
3.3线程安全问题
在模拟了网络延时的情况后,出现了
相同票 是由于原子性所导致的 piao–不是一个原子性的操作 原子性:不可在分割
0票或负票 由线程的唯一性所导致
出现数据安全问题,要满足按个条件
(1)要是多线程环境
(2)多线程在并发操作共享数据
(3)有多条语句在操作这个共享数据
可以使用同步代码块来解决线程安全问题
将有线程安全问题的代码包裹在synchronized
public class CellRunable implements Runnable {
int piao = 100;
static Object obj = new Object();//多个对象要用一把锁
@Override
public void run() {
while (true) {
//在实际购票时会有网络延迟,可以使用休眠来模拟网络延迟
synchronized (obj) {//锁,就是一个任意对象
if (piao > 1) {
try {
Thread.sleep(200);//模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售" + piao--);
}
}
//释放锁
}
}
}
public class MyText {
public static void main(String[] args) {
CellRunable runable1 = new CellRunable();
CellRunable runable2 = new CellRunable();
CellRunable runable3 = new CellRunable();
Thread th1 = new Thread(runable1);
Thread th2 = new Thread(runable1);
Thread th3 = new Thread(runable1);
//三个线程同时执行一个任务,所以不用加静态
th1.setName("窗口1");
th2.setName("窗口2");
th3.setName("窗口3");
th1.start();
th2.start();
th3.start();
}