并发与并行
并发:两个或者多个事件在同一时间段内发生(交替执行)
并行:两个g或者多个事件在同一时刻发生(同时发生)
进程:一个应用程序 点击一个应用程序进入到内存中占用一些内存运行,这些进入内存的程序叫做进程
线程:进程中的一个执行单元。线程属于进程,是进程中的一个执行单元,负责程序的执行
多线程的好处:1.效率高;
2.多个线程之间互不影响(在不同的占空间中)
java为抢占式调度
创建多线程程序的第一种方法
1.实现一个继承Thread的子类,重写Run方法
2.创建Thread子类的实例,即创建线程对象
3.调用线程对象的start()方法来启动线程
eg1:
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println("myThread->"+i);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Thread th=new MyThread();
th.start();
for(int i=0;i<10;i++) {
System.out.println("Main->"+i);
}
}
}
输出:
Main->0
Main->1
Main->2
myThread->0
myThread->1
myThread->2
myThread->3
myThread->4
myThread->5
myThread->6
myThread->7
myThread->8
myThread->9
Main->3
Main->4
Main->5
Main->6
Main->7
Main->8
Main->9
两个线程随机抢夺cpu.谁抢到谁执行,因此结果随机。
获取当前线程的名称:
System.out.println(Thread.currentThread().getName());
设置线程的名称:
th.setName("xiaozhnag");
或者
构造函数方式
sleep: 让当前执行的线程以毫秒为单位暂停
try {
Thread.sleep((long)1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
创建多线程程序的第二种方法:实现runable接口
步骤:
1.创建一个Runnable接口的实现类
2.在实现类中重写run方法,设置线程任务
3.创建一个Runnable接口的实现类对象
4.创建Thread对象,构造方法中传递Runnable接口的实现类对象
5,调用Thread类中的start方法,开启新的线程执行run方法
eg1:
class HisThread implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(i);
try {
Thread.sleep((long)1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class HelloWorld {
public static void main(String[] args) {
HisThread ru=new HisThread();
new Thread(ru).start();
}
}
使用Runnable方式的好处:
1.避免了单继承的局限性
一个类只能继承一个类,实现了Runnable接口,还可以继承其他类,实现了其他接口
2.增强了程序的扩展性,降低了程序的耦合
实现了Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
只需改变传入Thread的参数,就能改变执行的任务
匿名内部类实现线程的创建
第一种:
public class HelloWorld {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println("way1->"+i);
}
}
}.start();
}
}
第二种
public class HelloWorld {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println("way2->"+i);
}
}
}).start();
}
}
线程安全
-
单线程程序不会出现线程安全问题
-
多线程没有访问共享数据不会出现线程安全问题
-
多线程访问了共享数据会产生线程安全问题
eg.买票问题
class ticketRunnable implements Runnable{
private int ticket=100;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if(ticket>0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
ticket--;
}
else {
break;
}
}
}
}
public class SellTicket {
public static void main(String[] args) {
ticketRunnable run=new ticketRunnable();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
输出:
Thread-1窗口卖第100张票..
Thread-2窗口卖第100张票..
Thread-0窗口卖第100张票..
Thread-0窗口卖第97张票..
Thread-1窗口卖第97张票..
Thread-2窗口卖第97张票..
Thread-0窗口卖第94张票..
Thread-1窗口卖第94张票..
Thread-2窗口卖第92张票..
Thread-0窗口卖第91张票..
Thread-1窗口卖第91张票..
Thread-2窗口卖第89张票..
.....
Thread-0窗口卖第1张票..
Thread-1窗口卖第1张票..
Thread-2窗口卖第-1张票..
出现了线程安全问题,卖出了不存在的票和重复的票
解决线程安全问题的方式
方式一:使用同步代码块
格式:
synchronized (锁对象) {
可能出现线程安全问题的代码块(访问了共享数据的代码)
}
class ticketRunnable implements Runnable{
private int ticket=100;
Object ob=new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
synchronized (ob) {
if(ticket>0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
ticket--;
}
}
}
}
}
public class SellTicket {
public static void main(String[] args) {
ticketRunnable run=new ticketRunnable();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
方式2:使用同步方法
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
访问数据的代码;
}
eg.
class ticketRunnable implements Runnable{
private int ticket=100;
Object ob=new Object();
public synchronized void SellTicket() {
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
ticket--;
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
SellTicket();
}
}
}
public class SellTicket {
public static void main(String[] args) {
ticketRunnable run=new ticketRunnable();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
方式3:使用Lock锁
方法:
lock();
unlock();
eg.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ticketRunnable implements Runnable{
private int ticket=100;
Object ob=new Object();
Lock l=new ReentrantLock();//锁对象
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
l.lock();//上锁
if(ticket>0) {
try {
System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
ticket--;
} catch (Exception e) {
e.printStackTrace();
}finally{
l.unlock();//解锁
}
}else {
break;
}
}
}
}
public class SellTicket {
public static void main(String[] args) {
ticketRunnable run=new ticketRunnable();
Thread t1=new Thread(run);
Thread t2=new Thread(run);
Thread t3=new Thread(run);
t1.start();
t2.start();
t3.start();
}
}
线程之间的通信(等待/唤醒)
生产者消费者例子:
public class SellTicket {
static Object obj=new Object();
public static void main(String[] args) {
//创建顾客线程
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (obj) {
System.out.println("顾客买包子");
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("顾客吃包子");
}
}
}.start();
//创建老板线程
new Thread() {
public void run() {
System.out.println("老板做包子,需要5s");
synchronized (obj) {
try {
sleep(1000);
System.out.println("老板做好,唤醒顾客");
obj.notify();//唤醒顾客
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
}
}
等待唤醒机制
...
线程池
使用步骤:
1.使用线程池的工厂类Excutors里提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务。
3.调用ExecutorService中的submit方法,传递线程任务,开启线程,执行run方法
4.调用ExecutorService中的shutdown方法,关闭线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Runimp implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建了一个线程");
}
}
public class ThreadPool {
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService es=Executors.newFixedThreadPool(2);
es.submit(new Runimp());
es.submit(new Runimp());
es.submit(new Runimp());
es.shutdown();
}
}