多线程的学习
1、进程
1、进入到内存运行的程序就是进程
2、一个程序运行后至少有一个进程,一个进程可以包括多个线程
3、线程是进程的一个执行单元,负责程序的执行
2、线程调度
1、分时调度
所有线程轮流使用CPU,平均分配每个线程占用CPU的时间。
2、抢占式调度
优先让线程优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,java使用的就是抢占式调度
3、线程
1、多线程的创建
方法一:继承Thread类,重写run方法
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run: "+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 120; i++) {
System.out.println("main: "+i);
}
}
}
方法二:实现Runnable接口,重写run方法
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
方法三:使用匿名内部类的方法实现
package mythread;
public class ThreadDemo3 {
public static void main(String[] args) {
//1
new Thread(){
@Override
public void run() {
for (int i=0;i<30;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}.start();
//2
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i=0;i<30;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
};
new Thread(runnable).start();
// 3
new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<30;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}).start();
}
}
4、线程安全
多个线程同时抢夺同一个资源,导致的数据出错。
5、解决线程安全问题的方法
1、同步代码块:synchronized关键字可以用于方法的某个区块中,表示只对这个区块的资源进行互斥访问。
synchronized(同步锁对象){
需要同步操作的代码
}
注意:Object object = new Object(); 锁对象要是唯一的,要在run方法的外面,保证每个线程运行的时候看到的锁都是一样的。
private int ticket = 100;
Object object = new Object();
@Override
public void run() {
while (true){
synchronized (object){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在卖第"+ticket+"张票");
ticket --;
}
}
}
}
总结: t0抢到CPU执行权,拿到锁对象,并执行同步代码块的代码,t1发现没有同步锁对象,便会等待t0执行完并归还锁对象,才能继续执行,保证了只有一个线程在执行共享资源。保证了安全,但是判断锁、获取锁、释放锁,程序的效率会减低。
2、用this关键字代替同步锁对象
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
..................
}
runnable–mythread.threadSynchronized.RunnableImpl@1b6d3586
public void run() {
System.out.println("this---"+this);
...........
}
this—mythread.threadSynchronized.RunnableImpl@1b6d3586
可以看出run方法里的this和new出来的是同一个类,所以可以用this作为同步锁对象
synchronized (this){
// 代码
}
3、同步方法:用synchronized修饰的方法
写法一
public void run() {
System.out.println("this---"+this);
while (true){
ticketVoid();
}
}
public synchronized void ticketVoid(){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在卖第"+ticket+"张票");
ticket --;
}
}
写法二
public void run() {
System.out.println("this---"+this);
while (true){
ticketVoid();
}
}
public void ticketVoid(){
synchronized(this){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在卖第"+ticket+"张票");
ticket --;
}
}
}
4、静态同步:用本类的class文件对象作为同步锁对象
静态方法,因为this是创建对象之后产生的,静态方法优先于对象,静态方法的锁对象是本类的class文件对象(反射)
public static void ticketVoid(){
synchronized(RunnableImpl.class){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在卖第"+ticket+"张票");
ticket --;
}
}
}
5、Lock锁
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Lock l = new ReentrantLock();
@Override
public void run() {
while (true){
l.lock();
if (ticket>0){
try {
Thread.sleep(10);
System.out.println("正在卖第"+ticket+"张票");
ticket --;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();
}
}
}
}