一)进程与线程
进程: 正在进行的程序,是系统进行资源分配和调度的一个独立单位,
拥有独立的内存空间和系统资源
线程: 就是进程中一个负责程序执行的控制单元。
CPU调度和分派的基本单位。
多线程: 一个进程中可以有多个执行路径 称为多线程。
多线程就是最大化的提高CPU的利用率,
其他:
线程可与同属一个进程的其他的线程
共享进程所拥有资源;''
一个进程当中至少要有一个线程。
java程序至少两条线程(垃圾回收机制GC)
二) 创建线程的两种方法
1) extends Thread继承java.lang.Thread
2) 实现 Runnable实现java.lang.Runnable接口
三) 同步
同一时刻,只能有一个线程执行对应的 代码
synchronized (锁对象) { //线程拿到的锁对象是同一个
//同步的代码
}
四)部分常见类的线程安全性
List 有序的不唯一
ArrayList 动态数组 线程不安全
LinkedList 链表
Vector 动态数组 线程安全的
Set 无序的唯一
HashSet 无序的唯一
TreeSet 唯一的,有序的
HashMap 线程不安全的
Hashtable 线程安全的
StringBuffer 线程安全的
StringBuilder 线程不安
五)常用api
start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
yield()
暂停当前正在执行的线程对象,并执行其他线程。
sleep()
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
setPriority(int newPriority)
更改线程的优先级。
setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
join()
等待该线程终止
六)代码实现
package cn.edu.suda;
public class DuoXianchengTEst {
public static void main(String[] args) {
SellTickets mt1=new SellTickets();
mt1.setName("A窗口");
SellTickets mt2=new SellTickets();
mt2.setName("B窗口");
SellTickets mt3=new SellTickets();
mt3.setName("C窗口");
mt1.start();
mt2.start();
mt3.start();
/*AnotherThread at=new AnotherThread();
Thread t=new Thread(at);
t.start();*/
}
}
//设置线程类,继承Thread
class SellTickets extends Thread{
//设置票数为100
static int tickets=100;
//重写run方法
@Override
public void run() {
while(tickets>0){
try {
//延长运行时间,方便观察效果
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//保证同步,第一个if提高效率,减少拿到同步锁的次数
if(tickets>0){
//同步块里是类名。class确保锁唯一
synchronized (SellTickets.class) {
//第二个if确保不会是负数
if(tickets>0){
System.out.println(getName()+"卖出了"+tickets+"票");
tickets--;
}
}
}
}
}
}
/**
*
* 后面加的这个类是接口实现,二者用法大同小异,不过Thread类默认实现了一些方法
* 我在上面的main方法中将接口线程的使用注释掉,有兴趣可以自己尝试启动几个线程,main方法里开启一个主线程
* 然后你可能会发现两个线程交叉运行。。。
* 我刚才测试了一下,将SellTicket类里的sleep方法注释掉就能看见效果。
*
*/
class AnotherThread implements Runnable {
@Override
public void run() {
for (int i=0;i<1000;i++) {
System.out.println("输出"+i);
}
}
}
这是第二个例子,主要测试了线程常用的方法:
package cn.edu.suda;
import java.io.IOException;
public class Thread_some_method {
public static void main(String[] args) {
Mythread mt1=new Mythread();
mt1.setName("线程1");
Mythread mt2=new Mythread();
mt2.setName("线程2");
Mythread mt3=new Mythread();
mt3.setName("线程3");
/**
* 上面设置了三个线程,下面我们来验证多线程常用的几个方法
*
*/
/**
* join方法,该线程执行完,后面的线程才执行
* 注意使用方式,开启该线程后,先join,再开启其他线程,否则可能没效果
*/
// mt2.start();
// try {
// mt2.join();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// mt1.start();mt3.start();
/**
* yield()方法可以让当前正在执行的线程暂停,但它不会阻塞该线程,
* 它只是将该线程从运行状态转入就绪状态。只是让当前的线程暂停一下,
* 让系统的线程调度器重新调度一次。
* 很有可能,当某个线程调用了yield()方法暂停之后进入就绪状态,
* 它又马上抢占了CPU的执行权,继续执行。
* 【注意】
* 实际上,当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同,
* 或者优先级比当前线程更高的处于就绪状态的线程才会获得执行的机会。
* https://www.cnblogs.com/HigginCui/p/5903652.html
*/
// mt1.start();mt2.start();mt3.start();
// mt1.yield();
/**
* 关于守护线程与用户线程,我找了一篇博客
* http://blog.csdn.net/xyls12345/article/details/26256693
* 下面的例子也是按照博客上说的来,有兴趣自己代码实现
*
*/
mt1.setDaemon(true);
mt1.start();
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Mythread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(getName()+"第"+i+"次");
}
}
}